iT邦幫忙

2022 iThome 鐵人賽

DAY 20
1
Software Development

30 天與九頭蛇先生做好朋友系列 第 20

實作 Logout Provider

  • 分享至 

  • xImage
  •  

今天來實作應用程式啟動登出的流程。

首先先回顧一下時序圖:

Image.jpeg

從這張圖可以知道,今天主要要實作的是 Logout Provider。另外還有啟動登出的接口和回到應用程式的 callback 接口,總共三個。

實作前配置

先在 Laravel 新增相關的路由:

// Logout Provider
Route::get('/oauth2/logout', LogoutProvider::class)->name('oauth2.logout');

// 啟動 Logout 與 callback
Route::get('/logout', Logout::class)->name('logout');
Route::get('/logout/callback', LogoutCallback::class)->name('logout.callback');

再來是將這些路由註冊到 Hydra 上,首先是 Logout Provider,要在 hydra.yml 增加新的設定:

urls:
  logout: http://127.0.0.1:8000/oauth2/logout

再來昨天有提到,如果要回到應用程式的某個位置的話,就需要註冊應用程式的設定,Hydra 有提供管理應用程式的 API,更新對應的指令是 update,但它實際上運作是去呼叫 Admin API 的 PUT /clients/:id。HTTP PUT 方法會需要完整的參數內容,因此會需要把註冊的指令拿來修改一下,範例如下:

hydra --endpoint http://127.0.0.1:4445/ clients --skip-tls-verify \
      update my-rp \
      --grant-types authorization_code,implicit,client_credentials,refresh_token \
      --response-types "code,token,id_token,token code,code id_token,id_token token,id_token token code" \
      --scope openid \
      --token-endpoint-auth-method client_secret_basic \
      --callbacks "http://127.0.0.1:8000/callback" \
      --post-logout-callbacks "http://127.0.0.1:8000/logout/callback"

最後一行 --post-logout-callbacks 是新增加的。這個設定內容所代表的是,登出後要回到哪個應用程式的哪個位置。

應用程式啟動登出

再來是由應用程式啟動登出,程式如下:

// 取得之前登入的 ID Token
$idToken = $request->session()->get('id_token');

// 昨天所提到的,OpenID Connect 所定義的請求欄位
$query = Arr::query([
    'client_id' => 'my-rp',
    'id_token_hint' => $idToken,
    'post_logout_redirect_uri' => 'http://127.0.0.1:8000/logout/callback',
    'state' => '1a2b3c4d',
]);

// 產生 Logout 請求
$endSessionEndpoint = 'http://127.0.0.1:4444/oauth2/sessions/logout';

$LogoutRequest = $endSessionEndpoint . '?' . $query;

return Redirect::away($LogoutRequest);

最一開始會需要之前登入成功所拿到的 ID Token。其實之前已經有埋梗:「應用程式可以先把 ID Token 保存起來,未來會使用」,登出就是其中一個使用時機。

Logout 請求的欄位,雖然 OpenID Connect 蠻多都是使用「建議」關鍵字,但實際測試過 Hydra,如果不帶任何欄位的話是可行的。但有幾個欄位會提示要提供 id_token_hint。這點看原始碼可以了解,在沒有帶 id_token_hint 的時候,程式會認為使用者在操作「由 OP 發起的登出」,而這個情境下就不能使用 statepost_logout_redirect_uri 欄位。

處理登出請求

跟 Login Provider 或 Consent Provider 一樣,會先拿到 challenge 後,取得相關資訊確認,若覺得沒問題,就由 Provider 發訊息通知 Hydra 這個操作可接受。接著就可以拿到下一關的網址,最後再使用轉導即可。

Logout Provider 的實作如下:

$logoutChallenge = $request->get('logout_challenge');

// 取得 Logout 請求物件
$logoutRequest = $adminApi->getLogoutRequest($logoutChallenge);

Log::debug('Logout Request', json_decode((string)$logoutRequest, true));

// 接受 Logout 請求
$completedRequest = $adminApi->acceptLogoutRequest($logoutChallenge);

// 轉導回 Hydra
return Redirect::away($completedRequest->getRedirectTo());

其中 LogoutRequest 裡面相關的欄位如下:

欄位 說明
client 應用程式相關資訊
request_url 登出請求的原始 URL
rp_initiated 是否為 RP 發起的登出請求
sid Session ID
subject 使用者的唯一識別碼

sid 的用途之前曾賣過關子,是要用在登出的,主要是在確認使用者的狀態;而在完成登出請求的驗證後,接著就是移除 Session

登出流程的小差異

完成後,讀者實際測試的話,也許會發現跟上面講的有所出入。從瀏覽器的 Network 可以觀察到下面兩種情境:

  1. 應用程式發起登出 → Hydra → Logout Provider → Hydra → 應用程式 callback,這是上面所說的流程。
  2. 應用程式發起登出 → Hydra → 應用程式 callback。

會造成這兩個的差異的主要原因是,第一個是登入的時候有設定 remember 且時效還在的情境,第二個是登入沒有設定 remember 或時效已過。這部分可以這麼理解:因為使用 remember 代表要跟其他應用程式「共享」同一個登入狀態,因此相反的,在登出的時候可能會需要做特殊處理,因此才會導向 Logout Provider。

今天的程式可以參考 GitHub Commit,明天再接著講如何做 Backchannel Logout。


上一篇
由應用程式啟動登出流程
下一篇
通知應用程式登出
系列文
30 天與九頭蛇先生做好朋友23
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言